# coding:utf-8

"""
CopyRight(c)  2017-09-15  Fraplus Corp

Author: Weilin Shen, Xiaodong.yang
Email:  xiaodong.yang@unidt.com
Functions:

we chat server

dependence package:

fralog
handler

"""

import json
import os.path
import datetime
import sys
import string
import time
#
# 线程安全的队列
#
import queue
import threading

import pymongo
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.web import RequestHandler

import UploadSurvey as us
from fralog.FraLog import Fralog
# from fralog.kafka_logger import KafkaLogger
from wechat.wechat import WeChatHandler, WeChatHandlerNoJewellery

from handler.FraHandler import FraHandler
from handler.signalhandler import SigProccess

from scipy.stats import norm

host = '120.132.101.30'

log = Fralog().getFileLog()

#
# 本地缓存
#

# 问卷的缓存
survey_code = {}

#
# 答题规则缓存
#
select_survey = {}

#
# 写库任务队列
#
write_queue = queue.Queue()


class BResultHandler(FraHandler):  # 显示结果页
    """
    查看报告
    用户提交答题数据，进入该handler计算分数以及生成报告
    """

    def post(self):
        global select_survey
        # log.info('recv result request')
        self.set_header('content-type', 'json')
        #
        # 修改获取请求数据的方式
        params = self.request.body
        # log.info(params)
        data = json.loads(params.decode('utf-8'))
        code = data['code']
        # KafkaLogger().log_event(event='b_api', app='p_bole', user=params.get('user_id'), level='info',
        #                         msg=str(code) + '查看报告', status='200')
        #
        # 判断缓存是否命中
        #
        if select_survey.get(code) is None:
            db, client = us.get_db_client()
            selected_survey = db.surveys_collection.find_one({"code": code})
            select_survey[code] = selected_survey
        else:
            selected_survey = select_survey[code]

        # 计算大五人格各个维度的得分
        '''
        大五得分分为 O_beta, O_raw 以及 O_score
        O_beta: 问卷算出的直接得分，大五的归一化后的 beta 系数，这个值会之后记录到数据库，作为这次测验的得分
        O_raw：O_beta 映射到 分值范围 1-5 （模型 by 徐清）
        O_score：网页显示的得分，O_score 是 O_raw 的百分制得分，分值范围 0-100，这个数值只是在网页显示时使用，不会记录到数据库
        '''

        O_beta, C_beta, E_beta, A_beta, N_beta = self.calculateBigFiveScore(data)

        O_norm_mean = float(selected_survey['O_norm_mean'])
        C_norm_mean = float(selected_survey['C_norm_mean'])
        E_norm_mean = float(selected_survey['E_norm_mean'])
        A_norm_mean = float(selected_survey['A_norm_mean'])
        N_norm_mean = float(selected_survey['N_norm_mean'])

        O_norm_SD = float(selected_survey['O_norm_SD'])
        C_norm_SD = float(selected_survey['C_norm_SD'])
        E_norm_SD = float(selected_survey['E_norm_SD'])
        A_norm_SD = float(selected_survey['A_norm_SD'])
        N_norm_SD = float(selected_survey['N_norm_SD'])

        O_raw = self.score(O_beta, O_norm_mean, O_norm_SD)
        C_raw = self.score(C_beta, C_norm_mean, C_norm_SD)
        E_raw = self.score(E_beta, E_norm_mean, E_norm_SD)
        A_raw = self.score(A_beta, A_norm_mean, A_norm_SD)
        N_raw = self.score(N_beta, N_norm_mean, N_norm_SD)

        O_score_detail = self.give_score_detail(O_raw, O_norm_mean, O_norm_SD, 'o')
        C_score_detail = self.give_score_detail(C_raw, C_norm_mean, C_norm_SD, 'c')
        E_score_detail = self.give_score_detail(E_raw, E_norm_mean, E_norm_SD, 'e')
        A_score_detail = self.give_score_detail(A_raw, A_norm_mean, A_norm_SD, 'a')
        N_score_detail = self.give_score_detail(N_raw, N_norm_mean, N_norm_SD, 'n')

        O_score = round(20 * (O_raw - 0), 2)  # 得分从1-5分转为百分制
        C_score = round(20 * (C_raw - 0), 2)
        E_score = round(20 * (E_raw - 0), 2)
        A_score = round(20 * (A_raw - 0), 2)
        N_score = round(20 * (N_raw - 0), 2)

        O_norm_score = round(20 * (O_norm_mean - 0), 2)  # 常模均值从1-5分转为百分制
        C_norm_score = round(20 * (C_norm_mean - 0), 2)
        E_norm_score = round(20 * (E_norm_mean - 0), 2)
        A_norm_score = round(20 * (A_norm_mean - 0), 2)
        N_norm_score = round(20 * (N_norm_mean - 0), 2)
        # 传 大五每个维度的得分及得分说明，另外还有大五每个维度大家的得分（即常模），这些数据在结果页中需要用到。
        # self.render('result.html', O_score=str(O_score), O_score_detail=O_score_detail, C_score=str(C_score), C_score_detail=C_score_detail, E_score=str(E_score), E_score_detail=E_score_detail, A_score=str(A_score), A_score_detail=A_score_detail, N_score=str(N_score), N_score_detail=N_score_detail, O_norm_score=str(O_norm_score), C_norm_score=str(C_norm_score), E_norm_score=str(E_norm_score), A_norm_score=str(A_norm_score), N_norm_score=str(N_norm_score))

        # 将答案及得分保存到数据库
        response = json.dumps(data)
        response = json.loads(response)
        # code = response['code']
        # del(response['code'])

        bigFive = {}
        bigFive['O'] = O_raw
        bigFive['C'] = C_raw
        bigFive['E'] = E_raw
        bigFive['A'] = A_raw
        bigFive['N'] = N_raw
        norm = {}
        norm['O_norm_mean'] = O_norm_mean
        norm['C_norm_mean'] = C_norm_mean
        norm['E_norm_mean'] = E_norm_mean
        norm['A_norm_mean'] = A_norm_mean
        norm['N_norm_mean'] = N_norm_mean

        response_final = {}
        response_final['code'] = code
        response_final['response'] = response
        response_final['bigFive'] = bigFive
        response_final['norm'] = norm

        # 将数据保存到 responses_collection，包含问卷的 code，所有 question 的答案，加上大五各个维度的得分以及常模的五个维度的均值
        # db.responses_collection.insert(response_final)
        #
        # 写库部分进行优化，控制访问后台数据库的次数
        #
        write_queue.put(response_final)
        ret = []
        resp = {}
        resp['dim'] = 'o'
        resp['score'] = O_score
        resp['detail'] = O_score_detail
        resp['norm'] = O_norm_score
        ret.append(resp)

        resp = {}
        resp['dim'] = 'c'
        resp['score'] = C_score
        resp['detail'] = C_score_detail
        resp['norm'] = C_norm_score
        ret.append(resp)

        resp = {}
        resp['dim'] = 'e'
        resp['score'] = E_score
        resp['detail'] = E_score_detail
        resp['norm'] = E_norm_score
        ret.append(resp)

        resp = {}
        resp['dim'] = 'a'
        resp['score'] = A_score
        resp['detail'] = A_score_detail
        resp['norm'] = A_norm_score
        ret.append(resp)

        resp = {}
        resp['dim'] = 'n'
        resp['score'] = N_score
        resp['detail'] = N_score_detail
        resp['norm'] = N_norm_score
        ret.append(resp)

        self.write(json.dumps(ret))
        # 例：{'_id': ObjectId('596c39a7383d240f494917b7'), 'code': 'coffee', 'response': {'q1': 'C', 'q2': 'B', 'q3': 'D', 'q4': 'D', 'q5': 'E', 'q6': 'A', 'q7': 'C', 'q8': 'B', 'q9': 'B', 'q10': 'D'}, 'bigFive': {'O': 2.0, 'C': 4.0, 'E': 4.0, 'A': 3.5, 'N': 3.5}, 'norm': {'O_norm_mean': 3.25, 'C_norm_mean': 3.35, 'E_norm_mean': 3.23, 'A_norm_mean': 3.64, 'N_norm_mean': 2.81}}
        # TODO:保存用户更多信息，如：IP 地址，访问设备，测试时间及测试用时。

    """
    cum 与 score 函数用于 将 大五归一化后的 beta 值 转换为 1-5 的得分（by 徐清）
    """

    def cum(self, x):
        t = float(x)
        s = 0.5 + (3 * t - t ** 3) / 4
        return s

    def score(self, x, mean=3, sd=1, mode='b'):
        """
        归一后的 beta 值 与 1-5分值 之间的互相转换
        """
        if mode == 'b':
            percent = self.cum(x)
            a = norm.ppf(percent)
        else:
            a = x
        ori = mean + a * sd
        if ori > 5:
            ori = 5
        if ori < 1:
            ori = 1
        return ori

    def normalization(self, O, C, E, A, N):
        """
        值的归一化
        """
        bO = O / ((O ** 2 + C ** 2 + E ** 2 + A ** 2 + N ** 2) ** 0.5)
        bC = C / ((O ** 2 + C ** 2 + E ** 2 + A ** 2 + N ** 2) ** 0.5)
        bE = E / ((O ** 2 + C ** 2 + E ** 2 + A ** 2 + N ** 2) ** 0.5)
        bA = A / ((O ** 2 + C ** 2 + E ** 2 + A ** 2 + N ** 2) ** 0.5)
        bN = N / ((O ** 2 + C ** 2 + E ** 2 + A ** 2 + N ** 2) ** 0.5)
        return bO, bC, bE, bA, bN

    def give_score_detail(self, raw, norm_mean, norm_SD, dim='o'):
        detail = ''
        db, client = us.get_db_client()
        desc = db.desc_collection.find_one({"id": dim})
        title = ''

        if (raw < norm_mean - norm_SD):  # 得分低于常模减去一个标准差，那么这个得分 '低于平均水平'
            detail = desc['l_detail']
            title = desc['l_title']
            direct = -1
        elif (raw > norm_mean + norm_SD):
            title = desc['h_title']
            detail = desc['h_detail']
            direct = 1
        else:
            detail = desc['mean']
            title = '四面八方无死角'
            direct = 0

        return {'detail': detail, 'title': title, 'direct': direct}

    def calculateBigFiveScore(self, data=None):
        db, client = us.get_db_client()

        all_beta = []
        try:
            for key in data:  # each 是题目编号
                question = db.questions_collection.find_one(
                    {"question_id": key})  # 从数据表里根据 question_id 取出 "beta_O	beta_C	beta_E	beta_A	beta_N	是否人格题"

                resp = data[key]  # self.get_argument('q' + str(index))
                # log.debug('%s' % (resp))
                response = resp['answer']
                if (response == 'A'):  # 选 A 得1分
                    response = 1
                elif (response == 'B'):
                    response = 2
                elif (response == 'C'):
                    response = 3
                elif (response == 'D'):
                    response = 4
                elif (response == 'E'):
                    response = 5
                else:
                    response = 0
                if question['beta_O'] + question['beta_C'] + question['beta_E'] + question['beta_A'] + question[
                    'beta_N'] != '':  # beta 有值得时候才是有用的测试问题（性别年龄不是）
                    beta = [response, question['beta_O'], question['beta_C'], question['beta_E'], question['beta_A'],
                            question['beta_N'], question['是否人格题']]  # [选项，此题大五 beta 值，此题 是否人格题]
                    all_beta.append(beta)

            bO, bC, bE, bA, bN = 0.0
            for item in all_beta:
                if beta[6] == 1:
                    factor = 2.0  # 如果是人格题，加重权重，如 factor = 2.0
                else:
                    factor = 1.0

                bO += beta[0] * beta[1] * factor  # response * question['beta_O'] * factor
                bC += beta[0] * beta[2] * factor
                bE += beta[0] * beta[3] * factor
                bA += beta[0] * beta[4] * factor
                bN += beta[0] * beta[5] * factor
            print('大五得分(归一化的 beta值)：', self.normalization(bO, bC, bE, bA, bN))
            return self.normalization(bO, bC, bE, bA, bN)  # 归一化的值

        except Exception as e:
            return 0
